import * as BABYLON from '@babylonjs/core'
import { Engine, Scene } from '@babylonjs/core'
import { buildScene } from './buildScene.ts'
import { createMaterials } from './createMaterials.ts'
import { addCamera } from './addCamera.ts'
import HavokPhysics from '@babylonjs/havok'
import { AddGUIControls } from './GUIControls.ts'

const initHavok = async () => {
  const wasmBinary = await fetch('/HavokPhysics_es.wasm').then(res => res.arrayBuffer())
  return await HavokPhysics({ wasmBinary })
}

export class YkBabylonScene {
  /**
   * Create3D.vue加载时会自动创建该类的实例，只需要在该类的构造函数中完成场景的构建即可
   */
    // @ts-ignore
  scene: Scene
  engine: Engine
  canvas: HTMLCanvasElement
  brickMaterial: BABYLON.StandardMaterial | null = null
  marbleMaterial: BABYLON.StandardMaterial | null = null
  grassMaterial: BABYLON.StandardMaterial | null = null
  cloudMaterial: BABYLON.StandardMaterial | null = null
  fireMaterial: BABYLON.StandardMaterial | null = null
  roadMaterial: BABYLON.StandardMaterial | null = null
  woodMaterial: BABYLON.StandardMaterial | null = null
  concreteMaterial: BABYLON.StandardMaterial | null = null
  starFieldMaterial: BABYLON.StandardMaterial | null = null
  steelMaterial: BABYLON.StandardMaterial | null = null
  textColor = '#ffffff'
  customData: {
    selected?: string[], // 鼠标选中的物体
    railZs?: number[], // 轨道的z坐标列表
    pickableMeshes?: BABYLON.AbstractMesh[], // 可选的物体列表
  }
  createMaterial: (pic?: string, uScale?: number, vScale?: number) => BABYLON.StandardMaterial

  constructor(canvas: HTMLCanvasElement) {
    this.engine = new Engine(canvas, true)
    this.canvas = canvas
    this.customData = {}
    this.CreateFrameScene().then(scene => {
      this.scene = scene
      this.engine.runRenderLoop(() => {
        this.scene.render()
      })
    })
    this.createMaterial = (pic = '/assets/concrete.jpg', uScale: number = 1, vScale: number = 1) => {
      let _ = new BABYLON.StandardMaterial('concrete')
      const texture = new BABYLON.Texture(pic)
      texture.uScale = uScale
      texture.vScale = vScale
      _.diffuseTexture = texture
      _.specularPower = 10
      _.specularColor = new BABYLON.Color3(0.1, 0.1, 0.1)
      _.ambientColor = new BABYLON.Color3(0.2, 0.2, 0.2)
      _.freeze() // 冻结材质提升性能
      return _
    }
  }

  async CreateFrameScene(): Promise<Scene> {
    /**
     * 创建包含控制界面、3D场景、天空盒、光源、重力、碰撞检测的场景框架
     */
    this.scene = new Scene(this.engine)

    // 设置左手坐标系
    this.scene.useRightHandedSystem = false

    // 启用场景物理引擎和碰撞检测
    this.scene.collisionsEnabled = true
    this.scene.gravity = new BABYLON.Vector3(0, -9.81, 0) // 设置重力

    // 使用 Havok 物理引擎 - BabylonJS 8.x 方式
    try {
      console.log('正在初始化Havok物理引擎...')
      const havokInstance = await initHavok()
      const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance)
      this.scene.enablePhysics(this.scene.gravity, havokPlugin)
      console.log('Havok物理引擎初始化成功')
    } catch (error) {
      console.error('Havok物理引擎初始化失败:', error)
      console.warn('回退到默认物理引擎')

      // 回退到默认物理引擎
      try {
        this.scene.enablePhysics(this.scene.gravity)
        console.log('默认物理引擎初始化成功')
      } catch (fallbackError) {
        console.error('默认物理引擎初始化也失败了:', fallbackError)
        console.warn('继续运行但不使用物理引擎')
      }
    }

    new BABYLON.HemisphericLight('光源', new BABYLON.Vector3(1, 1, 1)) // 调整光照方向以适应Y向上坐标系
    createMaterials(this) // 材质必须在scene初始化以后才能创建
    buildScene(this)
    addCamera(this)
    AddGUIControls(this)

    const skybox = BABYLON.MeshBuilder.CreateBox('天空盒', { size: 10000.0 }, this.scene)
    const skyboxMaterial = new BABYLON.StandardMaterial('skyBox', this.scene)
    skyboxMaterial.backFaceCulling = false
    const files = [
      '/assets/skybox/skybox_px.jpg',
      '/assets/skybox/skybox_py.jpg',
      '/assets/skybox/skybox_pz.jpg',
      '/assets/skybox/skybox_nx.jpg',
      '/assets/skybox/skybox_ny.jpg',
      '/assets/skybox/skybox_nz.jpg'
    ]
    skyboxMaterial.reflectionTexture = BABYLON.CubeTexture.CreateFromImages(files, this.scene)
    skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE
    skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0)
    skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0)
    skyboxMaterial.freeze()
    skybox.material = skyboxMaterial

    return this.scene
  }
}